package forkjoin;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import extra166y.ParallelArray;
import extra166y.ParallelDoubleArray;
import extra166y.ParallelLongArray;
import extra166y.Ops.DoubleOp;

public class Main {

    private static final int PARALLELISM = 2;

    private static final int REPS = 500;

    private static final int SIZE = 1024 * 128;

    private static long[] forkDoubles = new long[SIZE];

    private static long[] singleDoubles = new long[SIZE];

    private static final List<Double> forks = new ArrayList<Double>();

    private static final List<Double> singles = new ArrayList<Double>();

    public static void main(String[] args) {
        ParallelArray.defaultExecutor().setParallelism(PARALLELISM);
        System.out.println("parallelism: " + ParallelArray.defaultExecutor().getParallelism());

        for (int i = 1; i <= REPS; i++) {
            runTest();
            if (i % 100 == 0) {
                printStats();
            }
        }

    }

    private static void buildDoubles() {
        for (int i = 0; i < SIZE; i++) {
            singleDoubles[i] = forkDoubles[i] = (long) (Math.random() * SIZE);
        }
    }

    private static void runTest() {
        buildDoubles();

        forks.add(doTimed(new TimedOp() {
            @Override
            public void doOp() {
                ParallelLongArray.createUsingHandoff(forkDoubles, ParallelArray.defaultExecutor()).sort();
            }
        }));

        singles.add(doTimed(new TimedOp() {
            @Override
            public void doOp() {
                Arrays.sort(singleDoubles);
            }
        }));
    }

    private static Double doTimed(TimedOp op) {
        long before = System.nanoTime();
        op.doOp();
        long after = System.nanoTime();
        double delta = after - before;
        return delta / (1000 * 1000);
    }

    private static interface TimedOp {
        void doOp();
    }

    private static void printStats() {
        double forkMean = printMeanAndDeviation(forks);
        double singleMean = printMeanAndDeviation(singles);
        System.out.println("~Speedup: " + (singleMean / forkMean));
        System.out.println("-");
    }

    private static double printMeanAndDeviation(List<Double> list) {
        ParallelDoubleArray array = ParallelDoubleArray.createEmpty(list.size(), ParallelArray.defaultExecutor());
        array.asList().addAll(list);
        final double mean = array.sum() / list.size();

        array.replaceWithMapping(new DoubleOp() {
            @Override
            public double op(double a) {
                return (a - mean) * (a - mean);
            }
        });

        final double deviation = Math.sqrt(array.sum() / list.size());
        System.out.println("Mean: " + mean + " StdDev: " + deviation);
        list.clear();
        return mean;
    }

}
